/***************************************************
**
**	DN3DString.m
**
**	By Brian Hobbs (brian@ny.shl.com)
**
**	This class actually creates the 3D text string.
**	It's performed through the PSW_tracestring call.
**	The rest of the algorithm is massaging the bezier
**	control points into the proper format to be 
**	recognized by RiPatchMesh, which physically
**	creates the text in Renderman.
**
**	This code is public domain.  Feel free to use
**	it in any way you wish.  I'd like to see any 
**	changes you make, tho.
**
****************************************************/

#import "DN3DString.h"
#import "CharPaths.h"
#import <ri/ri.h>

#define SCALE 100

#define MOVETO	-1000
#define LINETO	-2000
#define CURVETO -3000
#define CLOSE	-4000
#define EMPTY	-5000

@implementation DN3DString:N3DShape

- init
{
	int i;
	[super init];
	
	resolution[0] = 35;
	resolution[1] = 1;
	
	textFont = (char *)NXZoneMalloc([self zone], sizeof(char) * 256);
	for (i=0; i<256; i++)
		textFont[i] = 0;
		
	return self;
}

- setResolution:(float)newRes
{
	resolution[0] = newRes;
	return self;
}

- setTextString:(const char *)aString andFont:(const char *)aFont
{
	float pathStack[1000];
	int *numCurves, *counter;	
	int i,j,k;

	for (i=0; i<1000; i++)
		pathStack[i] = EMPTY;

	PSW_tracestring(aString, aFont, &(pathStack[0]));
	
	if (mesh){
		for (i=0; i<numPaths; i++)
			NXZoneFree([self zone], mesh[i]);
		NXZoneFree([self zone], mesh);
	}
	if (numCtrlPoints)
		NXZoneFree([self zone], numCtrlPoints);

	numPaths = i = 0;
	
	while (pathStack[i] != EMPTY){		// find the number of paths
		if (pathStack[i] == CLOSE)
			numPaths++;
		i++;
	}
		
	mesh = (RtFloat **)NXZoneMalloc([self zone], sizeof(RtFloat *) * numPaths);
	numCtrlPoints = (int *)NXZoneMalloc([self zone], sizeof(int) * numPaths);
	numCurves = (int *)NXZoneMalloc([self zone], sizeof(int) * numPaths);
	counter = (int *)NXZoneMalloc([self zone], sizeof(int) * numPaths);
	
	for (i=0; i<numPaths; i++){
		numCtrlPoints[i] = 0;
		numCurves[i] = 0;
		counter[i] = 0;
	}
	
	i = j = 0;
	
	// find the number of bezier control points of each path...
	
	while (pathStack[i] != EMPTY){
		if (pathStack[i] == LINETO || pathStack[i] == CURVETO)
			numCurves[j]++;
		if (pathStack[i] == CLOSE){
			numCtrlPoints[j] = (4 + ((numCurves[j] - 1) * 3));
			mesh[j] = (RtFloat *)NXZoneMalloc([self zone], sizeof(RtFloat) * (numCtrlPoints[j] * 2 * 3));
			j++;
		}
		i++;
	}
	
	j = 0;
	
	for (k = 0; k < 2; k++){
		i = j = 0;		
		while ((pathStack[i] != EMPTY) && (j != numPaths)){		// then properly fill the mesh
			switch((int)pathStack[i]){
				case LINETO :
					mesh[j][counter[j]] = (float)pathStack[i-2] / SCALE;
					mesh[j][++counter[j]] = (float)pathStack[i-1] / SCALE;
					mesh[j][++counter[j]] = (float)k / 4.0;
					mesh[j][++counter[j]] = (float)pathStack[i-2] / SCALE;
					mesh[j][++counter[j]] = (float)pathStack[i-1] / SCALE;
					mesh[j][++counter[j]] = (float)k / 4.0;
					counter[j]++;
					break;
				case CLOSE :
					j++;
					break;
				case MOVETO:
				case CURVETO:
					break;				
				default : 
					mesh[j][counter[j]] = (float)pathStack[i] / SCALE;
					counter[j]++;
					if (((counter[j] + 1) % 3) == 0){
						mesh[j][counter[j]] = (float)k / 4.0;
						counter[j]++;
					}
					break;
			}
			i++;
		}
	}
	
	NXZoneFree([self zone], numCurves);
	NXZoneFree([self zone], counter);
	
	return self;
}

- setTextFont:(const char *)aFont
{
	strcpy(textFont, (char *)aFont);
	
	return self;
}

- setTextString:(const char *)aString
{
// assumes setTextFont previously invoked

	[self setTextString:aString andFont:(const char *)textFont];
	
	return self;
}

- renderSelf:(RtToken)context
{
	int i;
	
	RiBasis(RiBezierBasis, RI_BEZIERSTEP, RiBezierBasis, RI_BEZIERSTEP);
	RiGeometricApproximation( RI_TESSELATION, RI_PARAMETRIC, resolution, RI_NULL );

//	RiRotate(-90,1,0,0);
	RiTranslate(0,0,-0.5);
	RiScale(0.15, 0.15, 0.15);

	for (i=0; i<numPaths; i++)
		RiPatchMesh(RI_BILINEAR, (RtInt)numCtrlPoints[i], RI_PERIODIC, (RtInt)2, RI_NONPERIODIC, RI_P, (RtPointer)mesh[i], RI_NULL);
    
	return self;
}
@end
  
